<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="zo6qG" id="zo6qG"><span data-lake-id="ufe93343f" id="ufe93343f">典型回答</span></h1>
  <p data-lake-id="u6ae0324c" id="u6ae0324c"><br></p>
  <p data-lake-id="u5bd2b6bb" id="u5bd2b6bb"><span data-lake-id="u41c0df04" id="u41c0df04">以下就是一个SpringBoot启动的入口，想要了解SpringBoot的启动过程，就从这里开始。</span></p>
  <p data-lake-id="ua8a10772" id="ua8a10772"><span data-lake-id="u2bfe00c8" id="u2bfe00c8">​</span><br></p>
  <pre lang="java"><code>
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
    	SpringApplication.run(Application.class, args);  也可简化调用静态方法
    }
}
</code></pre>
  <p data-lake-id="u82293c3e" id="u82293c3e"><span data-lake-id="u05afc2af" id="u05afc2af">​</span><br></p>
  <p data-lake-id="u07e5b6f6" id="u07e5b6f6"><span data-lake-id="ue48ea717" id="ue48ea717">这里我们直接看重重点的</span><code data-lake-id="u2c1d0aee" id="u2c1d0aee"><span data-lake-id="u39278e80" id="u39278e80">SpringApplication.run(Application.class, args);</span></code><span data-lake-id="ueb41ee6b" id="ueb41ee6b">方法。他的实现细节如下：</span></p>
  <p data-lake-id="u886f0b86" id="u886f0b86"><span data-lake-id="uc50bdf0c" id="uc50bdf0c">​</span><br></p>
  <pre lang="java"><code>
public static ConfigurableApplicationContext run(Object source, String... args) {
    return run(new Object[] { source }, args);
}

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
}
</code></pre>
  <p data-lake-id="u723b926e" id="u723b926e"><br></p>
  <p data-lake-id="uf94a2e65" id="uf94a2e65"><span data-lake-id="uc3afe1ae" id="uc3afe1ae">最终就是</span><code data-lake-id="u074f7eac" id="u074f7eac"><span data-lake-id="u0a112374" id="u0a112374">new SpringApplication(sources).run(args)</span></code><span data-lake-id="u7ca1298b" id="u7ca1298b">这部分代码了。那么接下来就需要分两方面介绍SpringBoot的启动过程。一个是</span><code data-lake-id="u1cbc147b" id="u1cbc147b"><span data-lake-id="u47639adc" id="u47639adc">new SpringApplication</span></code><span data-lake-id="u596c80a1" id="u596c80a1">的初始化过程，一个是</span><code data-lake-id="uceecec13" id="uceecec13"><span data-lake-id="u7e374e22" id="u7e374e22">SpringApplication.run</span></code><span data-lake-id="u479e0190" id="u479e0190">的启动过程。</span></p>
  <p data-lake-id="uf46957e2" id="uf46957e2"><span data-lake-id="u946629b0" id="u946629b0">​</span><br></p>
  <h3 data-lake-id="UkHiy" id="UkHiy"><span data-lake-id="u8faa2a5c" id="u8faa2a5c">new SpringApplication()</span></h3>
  <p data-lake-id="u94758c7e" id="u94758c7e"><br></p>
  <p data-lake-id="u3ae4c438" id="u3ae4c438"><span data-lake-id="u84220030" id="u84220030">在SpringApplication的构造函数中，调用了一个initialize方法，所以他的初始化逻辑直接看这个initialize方法就行了。流程图及代码如下：</span></p>
  <p data-lake-id="u3cbf3f0f" id="u3cbf3f0f"><span data-lake-id="u8906cafe" id="u8906cafe">​</span><br></p>
  <p data-lake-id="uedc05978" id="uedc05978"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1700289209958-a7eadd74-5f94-4e5a-bc95-b4fb9466a5c2.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_16%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <pre lang="java"><code>
public SpringApplication(Object... sources) {
    initialize(sources);
}

private void initialize(Object[] sources) {
    // 添加源：如果 sources 不为空且长度大于 0，则将它们添加到应用的源列表中
    if (sources != null &amp;&amp; sources.length &gt; 0) {
        this.sources.addAll(Arrays.asList(sources));
    }

    // 设置web环境：推断并设置 Web 环境（例如，检查应用是否应该运行在 Web 环境中）
    this.webEnvironment = deduceWebEnvironment();

    // 加载初始化器：设置 ApplicationContext 的初始化器，从 'spring.factories' 文件中加载 ApplicationContextInitializer 实现
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

    // 设置监听器：从 'spring.factories' 文件中加载 ApplicationListener 实现
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

    // 确定主应用类：通常是包含 main 方法的类
    this.mainApplicationClass = deduceMainApplicationClass();
}

</code></pre>
  <p data-lake-id="ud4a24276" id="ud4a24276"><br></p>
  <ol list="uaae8173a">
   <li fid="uac31781a" data-lake-id="u4d28e2e3" id="u4d28e2e3"><strong><span data-lake-id="u959b08db" id="u959b08db">添加源</span></strong><span data-lake-id="u59c7ba11" id="u59c7ba11">：将提供的源（通常是配置类）添加到应用的源列表中。</span></li>
   <li fid="uac31781a" data-lake-id="u36feb5b9" id="u36feb5b9"><strong><span data-lake-id="ua582b88d" id="ua582b88d">设置 Web 环境</span></strong><span data-lake-id="u03fb99d1" id="u03fb99d1">：判断应用是否应该运行在 Web 环境中，这会影响后续的 Web 相关配置。</span></li>
   <li fid="uac31781a" data-lake-id="u131c1a22" id="u131c1a22"><strong><span data-lake-id="u7e77ffa3" id="u7e77ffa3">加载初始化器</span></strong><span data-lake-id="ubec61596" id="ubec61596">：从 </span><code data-lake-id="u53a6a5a3" id="u53a6a5a3"><span data-lake-id="ub2f9e91f" id="ub2f9e91f">spring.factories</span></code><span data-lake-id="ucf740452" id="ucf740452"> 文件中加载所有列出的 </span><code data-lake-id="u07a2f6d6" id="u07a2f6d6"><span data-lake-id="ub8f82561" id="ub8f82561">ApplicationContextInitializer</span></code><span data-lake-id="uc12e2b7e" id="uc12e2b7e"> 实现，并将它们设置到 </span><code data-lake-id="u6e06375e" id="u6e06375e"><span data-lake-id="u862186b8" id="u862186b8">SpringApplication</span></code><span data-lake-id="u6a43aec8" id="u6a43aec8"> 实例中，以便在应用上下文的初始化阶段执行它们。</span></li>
   <li fid="uac31781a" data-lake-id="u58bf6eed" id="u58bf6eed"><strong><span data-lake-id="ufb741c61" id="ufb741c61">设置监听器</span></strong><span data-lake-id="u1dc4b3a0" id="u1dc4b3a0">：加载和设置 </span><code data-lake-id="u033dc496" id="u033dc496"><span data-lake-id="ue727c432" id="ue727c432">ApplicationListener</span></code><span data-lake-id="u20da6dfe" id="u20da6dfe"> 实例，以便应用能够响应不同的事件。</span></li>
   <li fid="uac31781a" data-lake-id="u6974afaf" id="u6974afaf"><strong><span data-lake-id="u4b334c71" id="u4b334c71">确定主应用类</span></strong><span data-lake-id="u3abbecc3" id="u3abbecc3">：确定主应用类，这个主应用程序类通常是包含 </span><code data-lake-id="u86c4b2fd" id="u86c4b2fd"><span data-lake-id="u68081830" id="u68081830">public static void main(String[] args)</span></code><span data-lake-id="ue01954a9" id="ue01954a9"> 方法的类，是启动整个 </span><code data-lake-id="ufe538dc0" id="ufe538dc0"><span data-lake-id="ue94eaf56" id="ue94eaf56">Spring Boot </span></code><span data-lake-id="u515cdb86" id="u515cdb86">应用的入口点。</span></li>
  </ol>
  <p data-lake-id="u4a8213f9" id="u4a8213f9"><br></p>
  <p data-lake-id="uc9331fc2" id="uc9331fc2"><strong><span data-lake-id="ub80a9e91" id="ub80a9e91">这里面第三步，加载初始化器这一步是Spring Boot的自动配置的核心</span></strong><span data-lake-id="ue8013b8b" id="ue8013b8b">，因为在这一步会从 spring.factories 文件中加载并实例化指定类型的类。</span></p>
  <p data-lake-id="u1f012d52" id="u1f012d52"><span data-lake-id="u224abcab" id="u224abcab">​</span><br></p>
  <p data-lake-id="uc053b783" id="uc053b783"><span data-lake-id="u4a020be7" id="u4a020be7">具体实现的代码和流程如下：</span></p>
  <p data-lake-id="udb14c9e5" id="udb14c9e5"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1700290239953-57bff073-5cfd-4007-b26f-0e9ddbafa1d1.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_32%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <pre lang="java"><code>
private &lt;T&gt; Collection&lt;? extends T&gt; getSpringFactoriesInstances(Class&lt;T&gt; type, Class&lt;?&gt;[] parameterTypes, Object... args) {
    // 获取当前线程的上下文类加载器
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    // 从spring.factories加载指定类型的工厂名称，并使用LinkedHashSet确保名称的唯一性，以防重复
    Set&lt;String&gt; names = new LinkedHashSet&lt;String&gt;(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

    // 创建指定类型的实例。这里使用反射来实例化类，并传入任何必要的参数
    List&lt;T&gt; instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);

    // 对实例进行排序，这里使用的是Spring的注解感知比较器，可以处理@Order注解和Ordered接口
    AnnotationAwareOrderComparator.sort(instances);

    // 返回实例集合
    return instances;
}
</code></pre>
  <p data-lake-id="u993240fb" id="u993240fb"><br></p>
  <p data-lake-id="u21ede2df" id="u21ede2df"><span data-lake-id="u008d0fe0" id="u008d0fe0">以下就是new SpringApplication的主要流程，主要依赖initialize 方法初始化 Spring Boot 应用的关键组件和配置。</span></p>
  <p data-lake-id="u1a03eaf1" id="u1a03eaf1"><span data-lake-id="u678cad2c" id="u678cad2c">​</span><br></p>
  <p data-lake-id="u2eac5f5a" id="u2eac5f5a"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1700290270548-c372f15d-ccf6-4236-b7fc-8d970233fdf4.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_47%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u06ac2ae5" id="u06ac2ae5"><br></p>
  <p data-lake-id="u3fe9ed66" id="u3fe9ed66"><span data-lake-id="uf82c5ca5" id="uf82c5ca5">这个过程确保了在应用上下文被创建和启动之前，所有关键的设置都已就绪，包括环境设置、初始化器和监听器的配置，以及主应用类的识别。</span></p>
  <p data-lake-id="u9a27b9f5" id="u9a27b9f5"><span data-lake-id="ub4bc5e2d" id="ub4bc5e2d">​</span><br></p>
  <p data-lake-id="u0e3b416f" id="u0e3b416f"><span data-lake-id="u3235b6af" id="u3235b6af">​</span><br></p>
  <h3 data-lake-id="T6cIm" id="T6cIm"><span data-lake-id="u7e0e68a6" id="u7e0e68a6">SpringApplication.run</span></h3>
  <p data-lake-id="u60a788c6" id="u60a788c6"><br></p>
  <p data-lake-id="ued0a5401" id="ued0a5401"><span data-lake-id="u7e310b60" id="u7e310b60">看完了new SpringApplication接下来就在看看run方法做了哪些事情。这个方法是 SpringApplication 类的核心，用于启动 Spring Boot 应用。</span></p>
  <p data-lake-id="uedea3794" id="uedea3794"><span data-lake-id="u036894c3" id="u036894c3">​</span><br></p>
  <p data-lake-id="u0a5c7182" id="u0a5c7182"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1700292941424-49b97eaa-c0b7-43cc-b933-5a93a612ce7b.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_42%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uf884c00a" id="uf884c00a"><span data-lake-id="ube24f595" id="ube24f595">​</span><br></p>
  <pre lang="java"><code>
public ConfigurableApplicationContext run(String... args) {
    // 创建并启动一个计时器，用于记录应用启动耗时
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();

    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;

    // 配置无头（headless）属性，影响图形环境的处理
    configureHeadlessProperty();

    // 获取应用运行监听器，并触发开始事件
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();

    try {
        // 创建应用参数对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 准备环境，包括配置文件和属性源
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 打印应用的 Banner
        Banner printedBanner = printBanner(environment);
        // 创建应用上下文
        context = createApplicationContext();
        // 创建失败分析器
        analyzers = new FailureAnalyzers(context);
        // 准备上下文，包括加载 bean 定义
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 刷新上下文，完成 bean 的创建和初始化
        refreshContext(context);
        // 刷新后的后置处理
        afterRefresh(context, applicationArguments);
        // 通知监听器，应用运行完成
        listeners.finished(context, null);
        // 停止计时器
        stopWatch.stop();
        // 如果启用了启动信息日志，记录应用的启动信息
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        //触发ApplicationStartedEvent 事件
        listeners.started(context);
        //调用实现了 CommandLineRunner 和 ApplicationRunner 接口的 bean 中的 run 方法
		callRunners(context, applicationArguments);
        // 触发 ApplicationReadyEvent 事件
        listeners.running(context);
        // 返回配置好的应用上下文
        return context;
    }
    catch (Throwable ex) {
        // 处理运行失败的情况
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
    }
}

</code></pre>
  <p data-lake-id="u99b772ae" id="u99b772ae"><br></p>
  <p data-lake-id="u9b1ef5ac" id="u9b1ef5ac"><span data-lake-id="u04a03cb4" id="u04a03cb4">以上的过程太复杂了，我们挑几个关键的介绍一下他们的主要作用。</span></p>
  <p data-lake-id="u4753635b" id="u4753635b"><span data-lake-id="ud8d332a0" id="ud8d332a0">​</span><br></p>
  <p data-lake-id="ue0d9791c" id="ue0d9791c"><strong><span data-lake-id="uf295578e" id="uf295578e">启动&amp;停止计时器</span></strong><span data-lake-id="u3ff295e0" id="u3ff295e0">：在代码中，用到stopWatch来进行计时。所以在最开始先要启动计时，在最后要停止计时。这个计时就是最终用来统计启动过程的时长的。最终在应用启动信息输出的实时打印出来，如以下内容：</span></p>
  <p data-lake-id="uf0d01a41" id="uf0d01a41"><span data-lake-id="ue09c06f9" id="ue09c06f9">​</span><br></p>
  <pre lang="java"><code>
2023-11-18 09:00:05.789  INFO 12345 --- [           main] com.hollis.myapp.Application            : Started Application in 6.666 seconds (JVM running for 7.789)
</code></pre>
  <p data-lake-id="u78aec04e" id="u78aec04e"><br></p>
  <p data-lake-id="u4ee15145" id="u4ee15145"><strong><span data-lake-id="ua4c22849" id="ua4c22849">获取和启动监听器：</span></strong><span data-lake-id="ubd3a9c67" id="ubd3a9c67">这一步从spring.factories中解析初始所有的SpringApplicationRunListener 实例，并通知他们应用的启动过程已经开始。</span></p>
  <p data-lake-id="u8e6e9295" id="u8e6e9295"><span data-lake-id="u6c8e85f8" id="u6c8e85f8">​</span><br></p>
  <blockquote data-lake-id="ua703721f" id="ua703721f">
   <p data-lake-id="u1cd2c12d" id="u1cd2c12d"><span data-lake-id="u54b8e49e" id="u54b8e49e">SpringApplicationRunListener 是 Spring Boot 中的一个接口，用于在应用的启动过程中的不同阶段提供回调。实现这个接口允许监听并响应应用启动周期中的关键事件。SpringApplicationRunListener 接口定义了多个方法，每个方法对应于启动过程中的一个特定阶段。这些方法包括：</span></p>
   <ol list="uc5c6cad4">
    <li fid="u87d6d63b" data-lake-id="uc8bf6e06" id="uc8bf6e06"><span data-lake-id="u3032905a" id="u3032905a">starting()</span><span data-lake-id="u835b2cc1" id="u835b2cc1">：在运行开始时调用，此时任何处理都未开始，可以用于初始化在启动过程中需要的资源。</span></li>
    <li fid="u87d6d63b" data-lake-id="u2c31fe6e" id="u2c31fe6e"><span data-lake-id="ucf5f06dc" id="ucf5f06dc">environmentPrepared()</span><span data-lake-id="ub58a9729" id="ub58a9729">：当 </span><span data-lake-id="u22044d69" id="u22044d69">SpringApplication</span><span data-lake-id="u4e3fd187" id="u4e3fd187"> 准备好 </span><span data-lake-id="uf3b59589" id="uf3b59589">Environment</span><span data-lake-id="ud3dee2b2" id="ud3dee2b2">（但在创建 </span><span data-lake-id="u7c64f2f6" id="u7c64f2f6">ApplicationContext</span><span data-lake-id="uf2fc4d0c" id="uf2fc4d0c"> 之前）时调用，这是修改应用环境属性的好时机。</span></li>
    <li fid="u87d6d63b" data-lake-id="u07209d6c" id="u07209d6c"><span data-lake-id="udc613685" id="udc613685">contextPrepared()</span><span data-lake-id="ufa3f06c8" id="ufa3f06c8">：当 </span><span data-lake-id="u52bdcd1e" id="u52bdcd1e">ApplicationContext</span><span data-lake-id="uf1422848" id="uf1422848"> 准备好但在它加载之前调用，可以用于对上下文进行一些预处理。</span></li>
    <li fid="u87d6d63b" data-lake-id="u57a7ba52" id="u57a7ba52"><span data-lake-id="u4a78e509" id="u4a78e509">contextLoaded()</span><span data-lake-id="ubf7a1b83" id="ubf7a1b83">：当 </span><span data-lake-id="uc93819a9" id="uc93819a9">ApplicationContext</span><span data-lake-id="u1bbc6081" id="u1bbc6081"> 被加载（但在它被刷新之前）时调用，这个阶段所有的 bean 定义都已经加载但还未实例化。</span></li>
    <li fid="u87d6d63b" data-lake-id="u64febcd3" id="u64febcd3"><span data-lake-id="u1aa4f055" id="u1aa4f055">started()</span><span data-lake-id="ubdc551ac" id="ubdc551ac">：在 </span><span data-lake-id="u86a178b2" id="u86a178b2">ApplicationContext</span><span data-lake-id="ufa970713" id="ufa970713"> 刷新之后、任何应用和命令行运行器被调用之前调用，此时应用已经准备好接收请求。</span></li>
    <li fid="u87d6d63b" data-lake-id="uc3f34503" id="uc3f34503"><span data-lake-id="u10095c43" id="u10095c43">running()</span><span data-lake-id="u394c0eed" id="u394c0eed">：在运行器被调用之后、应用启动完成之前调用，这是在应用启动并准备好服务请求时执行某些动作的好时机。</span></li>
    <li fid="u87d6d63b" data-lake-id="u39383630" id="u39383630"><span data-lake-id="u746003de" id="u746003de">failed()：如果启动过程中出现异常，则调用此方法。</span></li>
   </ol>
  </blockquote>
  <p data-lake-id="u4cf7b7c2" id="u4cf7b7c2"><br></p>
  <p data-lake-id="u190edecf" id="u190edecf"><strong><span data-lake-id="ua1d51bbd" id="ua1d51bbd">装配环境参数：</span></strong><span data-lake-id="u2998dcad" id="u2998dcad">这一步主要是用来做参数绑定的，prepareEnvironment 方法会加载应用的外部配置。这包括 application.properties 或 application.yml 文件中的属性，环境变量，系统属性等。所以，我们自定义的那些参数就是在这一步被绑定的。</span></p>
  <p data-lake-id="u9be8ff39" id="u9be8ff39"><span data-lake-id="u3cc4a8ee" id="u3cc4a8ee">​</span><br></p>
  <p data-lake-id="u507c5983" id="u507c5983"><strong><span data-lake-id="u007ea7a1" id="u007ea7a1">打印Banner：</span></strong><span data-lake-id="uea8ac300" id="uea8ac300">这一步的作用很简单，就是在控制台打印应用的启动横幅Banner。如以下内容：</span></p>
  <p data-lake-id="u1f9c54eb" id="u1f9c54eb"><span data-lake-id="u4084aaa2" id="u4084aaa2">​</span><br></p>
  <pre lang="java"><code>
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.5)
</code></pre>
  <p data-lake-id="ud67b4a25" id="ud67b4a25"><br></p>
  <p data-lake-id="u14350b25" id="u14350b25"><strong><span data-lake-id="uba8ac6ba" id="uba8ac6ba">创建应用上下文：</span></strong><span data-lake-id="u30503e05" id="u30503e05">到这一步就真的开始启动了，第一步就是先要创建一个Spring的上下文出来，只有有了这个上下文才能进行Bean的加载、配置等工作。</span></p>
  <p data-lake-id="u0dd1e4e1" id="u0dd1e4e1"><span data-lake-id="u1b6bec6b" id="u1b6bec6b">​</span><br></p>
  <p data-lake-id="u5e89de17" id="u5e89de17"><strong><span data-lake-id="u4c73d708" id="u4c73d708">准备上下文</span></strong><span data-lake-id="ud25cc180" id="ud25cc180">：这一步非常关键，很多核心操作都是在这一步完成的：</span></p>
  <p data-lake-id="ua0217a7c" id="ua0217a7c"><span data-lake-id="u1989a545" id="u1989a545">​</span><br></p>
  <pre lang="java"><code>
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {

    // 将environment设置到应用上下文中
    context.setEnvironment(environment);

    // 对应用上下文进行后处理（可能涉及一些自定义逻辑）
    postProcessApplicationContext(context);

    // 应用所有的ApplicationContextInitializer
    applyInitializers(context);

    // 通知监听器上下文准备工作已完成
    listeners.contextPrepared(context);

    // 如果启用了启动信息日志，则记录启动信息和配置文件信息
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // 向上下文中添加特定于 Spring Boot 的单例 Bean
    context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }

    // 加载应用的源（如配置类）
    Set&lt;Object&gt; sources = getSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[sources.size()]));

    // 通知监听器上下文加载已完成
    listeners.contextLoaded(context);
}

</code></pre>
  <p data-lake-id="u4be36dd3" id="u4be36dd3"><br></p>
  <p data-lake-id="u22a7484b" id="u22a7484b"><span data-lake-id="u7fe32970" id="u7fe32970">在这一步，会打印启动的信息日志，主要内容如下：</span></p>
  <pre lang="java"><code>
2023-11-18 09:00:00.123  INFO 12345 --- [           main] com.example.myapp.Application            : Starting Application v0.1.0 on MyComputer with PID 12345 (started by user in /path/to/app)
</code></pre>
  <p data-lake-id="u500eac85" id="u500eac85"><br></p>
  <p data-lake-id="u0f981950" id="u0f981950"><strong><span data-lake-id="u73713e4f" id="u73713e4f">刷新上下文</span></strong><span data-lake-id="ua3aec43e" id="ua3aec43e">：这一步，是Spring启动的核心步骤了，这一步骤包括了实例化所有的 Bean、设置它们之间的依赖关系以及执行其他的初始化任务。</span></p>
  <p data-lake-id="u4d2344c5" id="u4d2344c5"><span data-lake-id="u5946b16e" id="u5946b16e">​</span><br></p>
  <pre lang="java"><code>
@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 为刷新操作准备此上下文
		prepareRefresh();

		// 告诉子类刷新内部 bean 工厂
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// 为在此上下文中使用做好 bean 工厂的准备工作
		prepareBeanFactory(beanFactory);

		try {
			// 允许在上下文子类中对 bean 工厂进行后处理
			postProcessBeanFactory(beanFactory);

			// 调用在上下文中注册为 bean 的工厂处理器
			invokeBeanFactoryPostProcessors(beanFactory);

			// 注册拦截 bean 创建的 bean 处理器
			registerBeanPostProcessors(beanFactory);

			// 初始化此上下文的消息源
			initMessageSource();

			// 初始化此上下文的事件多播器
			initApplicationEventMulticaster();

			// 在特定上下文子类中初始化其他特殊 bean
			onRefresh();

			// 检查监听器 bean 并注册它们
			registerListeners();

			// 实例化所有剩余的（非懒加载）单例
			finishBeanFactoryInitialization(beanFactory);

			// 最后一步：发布相应的事件
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			// 销毁已经创建的单例以避免悬挂资源
			destroyBeans();

			// 重置“激活”标志
			cancelRefresh(ex);

			// 将异常传播给调用者
			throw ex;
		}

		finally {
			// 在 Spring 的核心中重置常见的内省缓存，因为我们可能不再需要单例 bean 的元数据...
			resetCommonCaches();
		}
	}
}

</code></pre>
  <p data-lake-id="u3abc76b3" id="u3abc76b3"><br></p>
  <p data-lake-id="u1604bb07" id="u1604bb07"><span data-lake-id="u01fd95c6" id="u01fd95c6">所以，这一步中，主要就是创建BeanFactory，然后再通过BeanFactory来实例化Bean。</span></p>
  <p data-lake-id="u3536916f" id="u3536916f"><span data-lake-id="u59263482" id="u59263482">​</span><br></p>
  <p data-lake-id="u13845b8c" id="u13845b8c"><span data-lake-id="uadcb516f" id="uadcb516f">但是，很多人都会忽略一个关键的步骤（网上很多介绍SpringBoot启动流程的都没提到），那就是Web容器的启动，及Tomcat的启动其实也是在这个步骤。</span></p>
  <p data-lake-id="u8e243f98" id="u8e243f98"><span data-lake-id="u2441714f" id="u2441714f">​</span><br></p>
  <p data-lake-id="u0ddb647e" id="u0ddb647e"><span data-lake-id="ufcc39dfa" id="ufcc39dfa">在refresh-&gt; onRefresh中，这里会调用到ServletWebServerApplicationContext的onRefresh中：</span></p>
  <p data-lake-id="uf5126ba3" id="uf5126ba3"><span data-lake-id="u926a4920" id="u926a4920">​</span><br></p>
  <pre lang="java"><code>
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}


private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null &amp;&amp; servletContext == null) {
        StartupStep createWebServer = getApplicationStartup().start("spring.boot.webserver.create");
        ServletWebServerFactory factory = getWebServerFactory();
        createWebServer.tag("factory", factory.getClass().toString());
        this.webServer = factory.getWebServer(getSelfInitializer());
        createWebServer.end();
        getBeanFactory().registerSingleton("webServerGracefulShutdown",
                new WebServerGracefulShutdownLifecycle(this.webServer));
        getBeanFactory().registerSingleton("webServerStartStop",
                new WebServerStartStopLifecycle(this, this.webServer));
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    initPropertySources();
}
</code></pre>
  <p data-lake-id="u4ab25a6c" id="u4ab25a6c"><span data-lake-id="ua1c73458" id="ua1c73458">​</span><br></p>
  <p data-lake-id="ubc16bfab" id="ubc16bfab"><span data-lake-id="ue4788984" id="ue4788984">这里面的&#x0;createWebServer方法中，调用到factory.getWebServer(getSelfInitializer());的时候，factory有三种实现，分别是JettyServletWebServerFactory、TomcatServletWebServerFactory、UndertowServletWebServerFactory这三个，默认使用TomcatServletWebServerFactory。</span></p>
  <p data-lake-id="ue97e1225" id="ue97e1225"><span data-lake-id="u5bfacb3f" id="u5bfacb3f">​</span><br></p>
  <p data-lake-id="u6513420a" id="u6513420a"><span data-lake-id="u488dec59" id="u488dec59">TomcatServletWebServerFactory的getWebServer方法如下，这里会创建一个Tomcat</span></p>
  <pre lang="java"><code>
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    Tomcat tomcat = new Tomcat();
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    for (LifecycleListener listener : this.serverLifecycleListeners) {
        tomcat.getServer().addLifecycleListener(listener);
    }
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}
</code></pre>
  <p data-lake-id="u36d53cf5" id="u36d53cf5"><span data-lake-id="uf3828cf3" id="uf3828cf3"></span></p>
  <p data-lake-id="u6cf3389e" id="u6cf3389e"><span data-lake-id="ud27efbbe" id="ud27efbbe">&#x0;在最后一步getTomcatWebServer(tomcat);的代码中，会创建一个TomcatServer，并且把他启动：</span></p>
  <p data-lake-id="ucea3ad8a" id="ucea3ad8a"><span data-lake-id="u70aface5" id="u70aface5">​</span><br></p>
  <pre lang="java"><code>
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() &gt;= 0, getShutdown());
}


public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
    initialize();
}
</code></pre>
  <p data-lake-id="ua24444e1" id="ua24444e1"><span data-lake-id="uf8706a7d" id="uf8706a7d"></span></p>
  <p data-lake-id="u53ec8961" id="u53ec8961"><span data-lake-id="u683c8fb9" id="u683c8fb9">&#x0;接下来在initialize中完成了tomcat的启动。</span></p>
  <p data-lake-id="ufbf2af95" id="ufbf2af95"><span data-lake-id="u57e5ced8" id="u57e5ced8">​</span><br></p>
  <p data-lake-id="udf6a75d9" id="udf6a75d9"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1700295160982-af970710-9c20-4fa9-a387-e6f129c28a25.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_29%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ub4ffd012" id="ub4ffd012"><br></p>
  <p data-lake-id="u604db731" id="u604db731"><br></p>
  <p data-lake-id="u76198b9b" id="u76198b9b"><span data-lake-id="u8af2e9d7" id="u8af2e9d7">最后，SpringBoot的启动过程主要流程如下：</span></p>
  <p data-lake-id="ubebd2384" id="ubebd2384"><span data-lake-id="u7d5f8cca" id="u7d5f8cca">​</span><br></p>
  <p data-lake-id="uf1689366" id="uf1689366"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1700295434553-30dfeeb3-2cee-4aa4-877a-f7ea8003e1dd.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_51%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
 </body>
</html>